﻿using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Diagnostics;

namespace Qreed.Diagnostics
{
    /// <summary>
    /// Event arguments for when the <see cref="LogLevel"/> of a <see cref="BaseLogger"/> has been changed.
    /// </summary>
    public class LoggerLevelChange : EventArgs
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="LoggerLevelChange"/> class.
        /// </summary>
        /// <param name="oldLevel">The old level.</param>
        /// <param name="newLevel">The new level.</param>
        internal LoggerLevelChange(LogLevel oldLevel, LogLevel newLevel)
        {
            _oldLevel = oldLevel;
            _newLevel = newLevel;
        }

        private LogLevel _oldLevel;
        /// <summary>
        /// Gets the old level.
        /// </summary>
        /// <value>The old level.</value>
        public LogLevel OldLevel
        {
            get { return this._oldLevel; }
        }

        private LogLevel _newLevel;
        /// <summary>
        /// Gets the new level.
        /// </summary>
        /// <value>The new level.</value>
        public LogLevel NewLevel
        {
            get { return this._newLevel; }
        }
    }

    /// <summary>
    /// Event argument for writting messages to the log.
    /// </summary>
    public class LoggerEventArgs : EventArgs
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="LoggerEventArgs"/> class.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="ex">The ex.</param>
        /// <param name="level">The level.</param>
        /// <param name="callingFunctionName">Name of the calling function.</param>
        internal LoggerEventArgs(string message, string details, Exception ex, LogLevel level, StackFrame stackFrame)
        {
            _timeStamp = DateTime.Now;
            _message = message;
            _details = details;
            _ex = ex;
            _level = level;
            _stackFrame = stackFrame;
        }

        private DateTime _timeStamp;
        /// <summary>
        /// Gets the time stamp.
        /// </summary>
        /// <value>The time stamp.</value>
        public DateTime TimeStamp
        {
            get { return this._timeStamp; }
        }

        private string _message;
        /// <summary>
        /// Gets the message.
        /// </summary>
        /// <value>The message.</value>
        public string Message
        {
            get { return this._message; }
        }

        private string _details;
        /// <summary>
        /// Gets the detail message. This value can be <c>null</c>.
        /// </summary>
        /// <value>The details.</value>
        public string Details
        {
            get { return this._details; }
        }

        private Exception _ex;
        /// <summary>
        /// Gets the exception associated with the message. This value can be <c>null</c>.
        /// </summary>
        /// <value>The exception</value>
        public Exception Ex
        {
            get { return this._ex; }
        }

        private LogLevel _level;
        /// <summary>
        /// Gets the level associated with the message.
        /// </summary>
        /// <value>The level.</value>
        public LogLevel Level
        {
            get { return this._level; }
        }

        /// <summary>
        /// Gets the name of the calling function. This value is null when the <see cref="StackFrame"/> isn't included.
        /// </summary>
        /// <value>The name of the calling function.</value>
        public string CallingFunctionName
        {
            get 
            {
                if (StackFrame == null)
                    return null;

                return string.Format("{0}.{1}", 
                                     StackFrame.GetMethod().ReflectedType.FullName, 
                                     StackFrame.GetMethod().Name);
            }
        }

        private StackFrame _stackFrame;
        /// <summary>
        /// Gets the stack frame. This value can be <c>null</c>.
        /// </summary>
        /// <value>The stack frame.</value>
        public StackFrame StackFrame
        {
            get { return this._stackFrame; }
        }

        /// <summary>
        /// Get the log string.
        /// </summary>
        /// <returns>A string containing all the log information.</returns>
        public string ToLogString()
        {
            StringBuilder sb = new StringBuilder();

            sb.Append(TimeStamp);
            sb.Append(" - ");
            sb.Append(Level.ToString());
            sb.Append(" - ");

            if (string.IsNullOrEmpty(CallingFunctionName))
                sb.AppendLine("No stacktrace.");
            else
                sb.AppendLine(CallingFunctionName);

            sb.AppendLine(Message);

            if (!string.IsNullOrEmpty(Details))
                sb.AppendLine(Details);

            return sb.ToString();
        }
    }

    /// <summary>
    /// The log level used by the <see cref="Qreed.Diagnostics.BaseLogger"/> class.
    /// </summary>
    public enum LogLevel : int
    {
        /// <summary>
        /// Debug level (all information).
        /// </summary>
        [Description("Display all messages.")]
        Debug = 0,
        /// <summary>
        /// Information level.
        /// </summary>
        [Description("Display information messages and higher.")]
        Info = 1,
        /// <summary>
        /// Warning level.
        /// </summary>
        [Description("Display warnings and error messages.")]
        Warning = 2,
        /// <summary>
        /// Error level.
        /// </summary>
        [Description("Display only error messages.")]
        Error = 3
    }
}
